package org.walkerljl.commons.invoke.support;

import org.walkerljl.commons.invoke.InvokeParam;
import org.walkerljl.commons.invoke.ServiceInvokeProxy;
import org.walkerljl.commons.log.Logger;
import org.walkerljl.commons.log.LoggerFactory;
import org.walkerljl.commons.util.JSONUtils;
import org.walkerljl.commons.util.ReflectUtils;

import java.util.List;
import java.util.Map;

/**
 * DefaultServiceInvokeProxy
 *
 * @author lijunlin
 */
public class DefaultServiceInvokeProxy implements ServiceInvokeProxy {

    private static final Logger LOGGER = LoggerFactory.getLogger(DefaultServiceInvokeProxy.class);

    @Override
    public Object invoke(String serviceInterface, String serviceImpl, String methodName) throws Exception {
        return invoke(serviceInterface, serviceImpl, methodName, false, null);
    }

    @Override
    public Object invoke(String serviceInterface, String serviceImpl, String methodName, boolean isStaticMethod) throws Exception {
        return invoke(serviceInterface, serviceImpl, methodName, isStaticMethod, null);
    }

    @Override
    public Object invoke(String serviceInterface, String serviceImpl, String methodName, Map<String, Object> otherParameterMap) throws Exception {
        return invoke(serviceInterface, serviceImpl, methodName, false, otherParameterMap);
    }

    @Override
    public Object invoke(String serviceInterface, String serviceImpl, String methodName,
                         boolean isStaticMethod, Map<String, Object> otherParameterMap) throws Exception {
        return invoke(serviceInterface, serviceImpl, methodName, null, isStaticMethod, otherParameterMap);
    }

    @Override
    public Object invoke(String serviceInterface, String serviceImpl, String methodName, String paramsJSONString) throws Exception {
        return invoke(serviceInterface, serviceImpl, methodName, paramsJSONString, false, null);
    }

    @Override
    public Object invoke(String serviceInterface, String serviceImpl, String methodName, String paramsJSONString, boolean isStaticMethod) throws Exception {
        return invoke(serviceInterface, serviceImpl, methodName, paramsJSONString, isStaticMethod, null);
    }

    @Override
    public Object invoke(String serviceInterface, String serviceImpl, String methodName, String paramsJSONString, Map<String, Object> otherParameterMap) throws Exception {
        return invoke(serviceInterface, serviceImpl, methodName, paramsJSONString, false, otherParameterMap);
    }

    @Override
    public Object invoke(String serviceInterface, String serviceImpl, String methodName,
                         String paramsJSONString,
                         boolean isStaticMethod, Map<String, Object> otherParameterMap) throws Exception {
        Object invokeResult = null;
        try {
            //抽取反射调用参数
            List<InvokeParam> invokeParams = JSONUtils.parseList(paramsJSONString, InvokeParam.class);
            Class<?>[] paramClasses = null;
            Object[] params = null;
            if (invokeParams != null && invokeParams.size() != 0) {
                int paramsLength = invokeParams.size();
                paramClasses = new Class<?>[paramsLength];
                params = new Object[paramsLength];
                for (int i = 0; i < paramsLength; i++) {
                    InvokeParam invokeParam = invokeParams.get(i);
                    Class<?> typeClass = getParamClass(invokeParam.getType());
                    paramClasses[i] = typeClass;
                    params[i] = JSONUtils.parseObject(invokeParam.getValue(), typeClass);
                }
            }

            //反射调用
            if (isStaticMethod) {
                invokeResult = ReflectUtils.invokeDeclared(Class.forName(serviceInterface), methodName, paramClasses, params);
            } else {
                Object invokeObject = null;
                if (serviceImpl == null || "".equals(serviceImpl.trim())) {
                    invokeObject = getInvokeObject(Class.forName(serviceInterface), otherParameterMap);
                } else {
                    invokeObject = Class.forName(serviceImpl).newInstance();
                }
                invokeResult = ReflectUtils.invokeDeclared(invokeObject, methodName, paramClasses, params);
            }

            if (LOGGER.isDebugEnabled()) {
                LOGGER.debug(String.format("Invoked,serviceInterface:%s,serviceImpl:%s,methodName:%s,paramsJSONString:%s,isStaticMethod:%s,otherParameterMap:%s->%s.",
                        serviceInterface, serviceImpl, methodName, paramsJSONString, isStaticMethod,
                        JSONUtils.toJSONString(otherParameterMap), JSONUtils.toJSONString(invokeResult)));
            }
        } catch (Exception e) {
            invokeResult = "Fail to service invocation.";
            LOGGER.error(String.format("Invoked,serviceInterface:%s,serviceImpl:%s,methodName:%s,paramsJSONString:%s,isStaticMethod:%s,otherParameterMap:%s->%s.",
                    serviceInterface, serviceImpl, methodName, paramsJSONString, isStaticMethod,
                    JSONUtils.toJSONString(otherParameterMap), invokeResult), e);
            throw e;
        }
        return invokeResult;
    }

    /**
     * 获取参数Class
     *
     * @param type
     * @return
     * @throws ClassNotFoundException
     */
    private Class<?> getParamClass(String type) throws ClassNotFoundException {
        if (byte.class.getName().equals(type)) {
            return byte.class;
        } else if (short.class.getName().equals(type)) {
            return short.class;
        } else if (int.class.getName().equals(type)) {
            return int.class;
        } else if (long.class.getName().equals(type)) {
            return long.class;
        } else if (float.class.getName().equals(type)) {
            return float.class;
        } else if (double.class.getName().equals(type)) {
            return double.class;
        } else if (char.class.getName().equals(type)) {
            return char.class;
        } else if (boolean.class.getName().equals(type)) {
            return boolean.class;
        }
        return Class.forName(type);
    }

    @Override
    public Object getInvokeObject(Class<?> serviceInterfaceClass, Map<String, Object> otherParameterMap) {
        return null;
    }
}
